#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <cstdlib>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <fcntl.h>
#include <cassert>
#include <sys/epoll.h>

#include "threadPool.hpp"
#include "httpConn.hpp"
#include "../MultiThreadProgramming/locker.hpp"
using namespace std;

extern int addFd(int epollFd, int fd, bool oneShot);
extern int removeFd(int epollFd, int fd);

const int MAX_FD = 65536;
const int MAX_EVENT_NUMBER = 10000;

void addSig(int sig, void(handler)(int), bool restart = true) {
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = handler;
    if(restart) {
        sa.sa_flags |= SA_RESTART;
    }
    sigfillset(&sa.sa_mask);
    assert(sigaction(sig, &sa, nullptr) != -1);
}

void show_error(int connFd, const char* info) {
    cout << info << endl;
    send(connFd, info, strlen(info), 0);
    close(connFd);
}

int main(int argc, char* argv[]) {
    if (argc <= 2) {
        cout << 'Usage: ' << argv[0] << " <ip> <port>" << endl;
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi(argv[2]);

    addSig(SIGPIPE, SIG_IGN);

    threadPool<httpConn>* pool = nullptr;
    try {
        pool = new threadPool<httpConn>;
    }
    catch(...) {
        return 1;
    }

    httpConn* users = new httpConn[MAX_FD];
    assert(users);
    int userCount = 0;

    int listenFd  = socker(AF_INET, SOCK_STREAM, 0);
    assert(listenFd > 0);
    struct linger tmp = {1, 0};
    setsockopt(listenFd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp));

    int ret = 0;
    struct sockaddr_in address;
    memset(&address, 0, sizeof(address));
    address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &address.sin_addr);
    address.sin_port = htons(port);

    ret = bind(listenFd, (struct address*)&address, sizeof(address));
    assert(ret >= 0);

    ret = listen(listenFd, 5);
    assert(ret >= 0);

    epoll_event events[MAX_EVENT_NUMBER];
    int epollFd = epoll_create(5);
    assert(epollFd != -1);
    addFd(epollFd, listenFd, false);
    httpConn::m_epollFd = epollFd;

    while(true) {
        int number = epoll_wait(epollFd, events, MAX_EVENT_NUMBER, -1);
        if(number < 0 && errno != EINTR) {
            cout << "epoll failure" << endl;
            return 1;
        }
        for(int i = 0; i < number; ++i) {
            int sockFd = events[i].data.fd;
            if(sockFd == listenFd) {
                struct sockaddr_in clientAddr;
                socklen_t clientAddrLen = sizeof(clientAddr);
                int connFd = accept(listenFd, (struct sockaddr*)&clientAddr, &clientAddrLen);
                if(connFd < 0) {
                    cout << "errno is: " << errno << endl;
                    continue;
                }
                if(httpConn::m_user_count >= MAX_FD) {
                    show_error(connFd, "Internal server busy");
                    continue;
                }
                users[connFd].init(connFd, clientAddr);
            }
            else if (events[i].events & EPOLLRDHUP | EPOLLHUP | EPOLLERR) {
                users[sockFd].closeConn();
            }
            else if (events[i].events & EPOLLIN) {
                if(users[sockFd].read()) {
                    pool->append(users + sockFd);
                }
                else {
                    users[sockFd].closeConn();
                }
            }
            else if (events[i].events & EPOLLOUT) {
                if(!users[sockFd].write()) {
                    users[sockFd].closeConn();
                }
            }
            else {

            }
        }
    }
    
    close(epollFd);
    close(listenFd);
    delete[] users;
    delete pool;
    return 0;
}